package com.gitee.kamismile.gatewayAgent.server.bootstrap;

import com.gitee.kamismile.stone.commmon.util.SnowflakeIdWorker;
import com.gitee.kamismile.stone.commmon.util.ValueUtils;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import com.gitee.kamismile.gatewayAgent.server.handler.GateWayServerInitializer;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by lidong on 2017/2/8.
 */
@Component
public class GateWayServer {

    static final boolean SSL = System.getProperty("ssl") != null;

    @Autowired
    GateWayServerInitializer gateWayServerInitializer;

    @Autowired
    SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    GatewayAgentConstant gatewayAgentConstant;

    private Channel channel;

    public void start() throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }
        String os = System.getProperty("os.name");

        Map<String, Object> option = new HashMap<String, Object>();
        option.put("snowflakeIdWorker", snowflakeIdWorker);
        option.put("router", gatewayAgentConstant.getRouter());
        option.put("timeOut", gatewayAgentConstant.getTimeOut());

        EventLoopGroup bossGroup;
        EventLoopGroup workerGroup;

        Integer bossThreads = ValueUtils.isIntegerNull(gatewayAgentConstant.getBossThreads(), Runtime.getRuntime().availableProcessors());
        Integer workerThreads = ValueUtils.isIntegerNull(gatewayAgentConstant.getWorkerThreads(), Runtime.getRuntime().availableProcessors() * 2);

        if (os.startsWith("win") || os.startsWith("Win")) {
            bossGroup = new NioEventLoopGroup(bossThreads, new DefaultThreadFactory("agent-boss", true));
            workerGroup = new NioEventLoopGroup(workerThreads, new DefaultThreadFactory("agent-worker", true));
        } else {
            bossGroup = new EpollEventLoopGroup(bossThreads, new DefaultThreadFactory("agent-boss", true));
            workerGroup = new EpollEventLoopGroup(workerThreads, new DefaultThreadFactory("agent-worker", true));
        }


        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup);
            if (os.startsWith("win") || os.startsWith("Win")) {
                b.channel(NioServerSocketChannel.class);
            } else {
                b.channel(EpollServerSocketChannel.class)
                        .option(EpollChannelOption.SO_REUSEPORT, true);
            }
            b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 120000)
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childOption(ChannelOption.TCP_NODELAY, false)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
            ;
            gateWayServerInitializer.setSslCtx(sslCtx);
            b.handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(gateWayServerInitializer);

            channel = b.bind(gatewayAgentConstant.getInetHost(), gatewayAgentConstant.getPort()).sync().channel();
            channel.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }


}
